// ConsumingScanHandler.java
package com.ibm.tspaces.examples.handler;

import java.util.*;
import com.ibm.tspaces.*;
import com.ibm.tspaces.server.*;
import com.ibm.tspaces.server.handler.*;

/**************************************************************************
** ConsumingScanHandler  **
***************************/
/**
** This class implements an extended version of the consumingScan
** command which allows you to specify a limit on the number of
** tuples returned.
**
** @author Phil Estes
*/

public class ConsumingScanHandler extends TSHandler {

  /**
   ** the extendable factory
   **/
  private static final String FACTORYNAME = "com.ibm.tspaces.server.handler.TSFExtendable";

  /**
   ** the new command we are implementing - a n-limited consuming
   ** scan
   **/
  public static final String NCONSUMINGSCAN = "nConsumingScan";

  /**
   **
   ** the access attribute required for doing a command which writes
   **/
  public static final AccessAttribute write[] = { AccessAttribute._WRITE_ATTRIBUTE};

  /**
   ** no arg constructor
   **/
  public ConsumingScanHandler() { }

  /**
   ** answer whether we handle this kind of command..since we will be registered
   ** for only one command we will only be asked about NCONSUMINGSCAN so we can return
   ** true
   **/
  public final boolean handles(String commandString, SuperTuple argTuple) {
    return true;
  }

  /**
   ** answer what access attributes are necessary to perform our commands
   **/
  public AccessAttribute []
    attributes(String cmdString, SuperTuple argTuple) throws TSHandlerException {

    if( cmdString.equals(NCONSUMINGSCAN) )
      return write;
    else
      throw new TSHandlerException("I don't do command: "+cmdString);

  } // attributes


  /*
   ** ************************************************************************
   ** ** command **
   ** *************
   */

  /**
   ** Implement one or more "commands" (usually one).
   **
   ** @param ts The TupleSpace instance on which the command is executed.
   ** @param cmdString The cmd to be invoked
   ** @param argTuple The argument for the command.
   ** @param clientID_  The client Transaction ID
   ** @param communicator_ the io handle back to the client
   ** @param user_ the user issuing the command
   ** @return a "result" tuple that is the output of the command.
   ** @exception TSHandlerException if this handler cannot complete the command
   ** or there was some problem encountered while executing the
   ** command.
   **
   ***************************************************************************
   */

  public SuperTuple command( TS ts,
                             String cmdString,
                             SuperTuple argTuple,
                             String clientID_,
                             TSResponse communicator_,
                             String user_ ) throws TSHandlerException
  {

    SuperTuple retValue = new Tuple();
    try {
      if (cmdString.equals(NCONSUMINGSCAN))  {
        Integer cnt = (Integer)argTuple.getField(0).getValue();
        Tuple template = (Tuple)argTuple.getField(1).getValue();
        retValue = consumingScan(ts, cnt.intValue(), clientID_, template);
      } else {
        throw new TSHandlerException("Can't handle command "+cmdString);
      }
    } catch(TupleSpaceException e) {
      throw new TSHandlerException("Caught tuplespace exception",e);
    }

    return retValue;
  } // command


  /**
   ** Performs the parameter-limited consuming scan.
   **
   ** @param ts The TupleSpace instance on which the command is executed.
   ** @param num The number of tuples to consume in the scan
   ** @param clientID  The client Transaction ID
   ** @param argTuple The tuple template to match in the scan
   ** @return a "result" tuple that is a tuple of tuples of consumed tuples
   **
   ***************************************************************************
   */
  public SuperTuple consumingScan(TS ts, int num, String clientID, SuperTuple argTuple) {

    Tuple retTuple = new Tuple();
    try {
      //get the TSpaces database object
      TSDB tsdb = ts.getTSDB();
      //run a "regular" scan against the space
      SuperTuple tupleScan = ts.command(TupleSpace.SCAN, argTuple, clientID, null, TS._SYSTEM_USER);
      //count how many were returned
      int numMatches = tsdb.countTuple(clientID, argTuple );
      //now, either we return the lesser of the user request and the number of tuples available
      int max = numMatches < num ? numMatches : num;
      //iterate over the tuple list and perform the DB delete
      for (int i = 0; i < max; i++) {
        //get each tuple and remove it from the DB
        SuperTuple t = (SuperTuple)tupleScan.getField(i).getValue();
        TupleID tid = t.getTupleID();
        tsdb.deleteTupleById(clientID, tid);
        //as we delete each tuple, add it to the list we will return to the user
        retTuple.add(t);
      }

    } catch (TupleSpaceException tse) {
      System.out.println(tse);
      tse.printStackTrace();
    }
    return retTuple;
  }

  /**
   ** Test main routine to install the handler
   **/
  public final static void main( String[] args) {

    String host = "localhost";
    String spaceName = "consume";

    // We need to have admin authorithy.  Make sure the userid and password
    // are correct.
    String userid = "sysadmin";
    String password = "admin";

    TupleSpace ts = null;
    try {
      //create a space to work on
      // let it be created by anonymous.
      ts = new TupleSpace(spaceName, host);
      TupleSpace.cleanup();
      // now reaccess it as admin user so we can add 
      // a handler.
      ts = new TupleSpace(spaceName,
                          host,TupleSpace.DEFAULTPORT,
                          null,null,
                          userid,password);

      // Add a Factory that can be extended with new commands
      ts.addFactory( new Class[] { Class.forName(FACTORYNAME) } );

      // now let's add it to the space
      ts.addHandler( NCONSUMINGSCAN, new Class[] { ConsumingScanHandler.class } );

    } catch(Exception e) {
      System.out.println("exception occurred while installing handler: "+e);
      e.printStackTrace();
    }
  }


}

